package com.gx.service.impl;


import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.github.xiaoymin.knife4j.core.util.CollectionUtils;
import com.gx.common.config.flowable.FlowServiceFactory;
import com.gx.common.constant.ProcessConstants;
import com.gx.common.enums.FlowCommentEnum;
import com.gx.common.exception.CommonException;
import com.gx.model.dto.definition.FlowCommentDto;
import com.gx.model.dto.task.StartFlowTaskInfoDto;
import com.gx.model.pojo.TbBusinessTask;
import com.gx.model.pojo.TbFlowEnd;
import com.gx.model.pojo.TbFlowStart;
import com.gx.service.FlowInfoService;
import com.gx.service.FlowInstanceService;
import com.gx.service.FlowTaskService;
import com.gx.service.TbBusinessTaskService;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.EndEvent;
import org.flowable.bpmn.model.Process;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * @Author Liu-pj
 * @Date 2022/8/24 15:29
 * @Desc 流程实例操作
 **/
@Service
@Slf4j
public class FlowInstanceServiceImpl extends FlowServiceFactory implements FlowInstanceService {
    @Autowired
    private FlowInfoService flowInfoService;

    @Autowired
    private TbBusinessTaskService taskFormService;

    @Autowired
    private FlowTaskService flowTaskService;

    /**
     * 根据流程定义ID启动流程实例
     */
    @Override
    public void startProcessInstanceById(StartFlowTaskInfoDto dto) {
        String procDefId = dto.getProcDefId();
        String startUserId = dto.getUserId();
        Map<String, Object> variables = dto.getVariables();
        JSONObject startInfo = new JSONObject();
        String dataId = dto.getDataId();
        String businessKey = dto.getBusinessKey();
        startInfo.put("startUserId", startUserId);
        startInfo.put("startUserName", dto.getUserName());
        startInfo.put("businessKey", businessKey);
        startInfo.put("dataId", dataId);
        startInfo.put("extra", dto.getExtra());
        try {
            ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                    .processDefinitionId(procDefId)
                    .singleResult();
            if (Objects.nonNull(processDefinition) && processDefinition.isSuspended()) {
                throw new CommonException("流程已被挂起");
            }
            // 设置流程发起人Id到流程中
            identityService.setAuthenticatedUserId(startUserId);
            //1.2、设置可以自动跳过
            variables.put(ProcessConstants.FLOWABLE_SKIP_EXPRESSION_ENABLED, true);
            variables.put(ProcessConstants.PROCESS_INITIATOR, startUserId);
            variables.put("startInfo", startInfo);
            ProcessInstance processInstance = runtimeService.startProcessInstanceById(procDefId, variables);
            //设置实例id
            runtimeService.setProcessInstanceName(processInstance.getProcessInstanceId(), processDefinition.getName());
            //放行首个节点
            Task task = taskService.createTaskQuery()
                    .processInstanceId(processInstance.getProcessInstanceId())
                    .singleResult();
            if (Objects.nonNull(task)) {
                taskService.setAssignee(task.getId(), startUserId);
                FlowCommentDto commentDto = new FlowCommentDto();
                commentDto.setMessage(startUserId + ": 发起申请");
                String common = JSONObject.toJSONString(commentDto);
                //放行第一个节点 ,供流程重启使用
                taskService.addComment(task.getId(), processInstance.getProcessInstanceId(), FlowCommentEnum.START.getType(), common);
                taskService.complete(task.getId());
                //保存流程启动信息
                saveStartInsInfo(businessKey, dataId, dto.getUserName(), processInstance);
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("异常信息：{}，异常类型：{}", e.getMessage(), e.getClass());
            throw new CommonException("流程启动错误~");
        }
    }

    /**
     * 删除流程实例ID
     *
     * @param instanceId   流程实例ID
     * @param deleteReason 删除原因
     */
    @Override
    public void delete(String instanceId, String deleteReason) {
        // 查询历史数据
        HistoricProcessInstance historicProcessInstance =
                historyService.createHistoricProcessInstanceQuery().processInstanceId(instanceId).singleResult();
        if (Objects.isNull(historicProcessInstance)) {
            log.warn("流程实例不存在:{}" + instanceId);
        }
        if (historicProcessInstance != null) {
            //已经结束只删除历史
            if (historicProcessInstance.getEndTime() == null) {
                runtimeService.deleteProcessInstance(instanceId, deleteReason);
                historyService.deleteHistoricProcessInstance(historicProcessInstance.getId());
            } else {
                historyService.deleteHistoricProcessInstance(historicProcessInstance.getId());
            }
        }
    }

    /**
     * 流程实例挂起或激活
     *
     * @param instanceId 实例id
     */
    @Override
    public void updateState(String instanceId) {
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(instanceId)
                .singleResult();
        if (ObjectUtil.isNotNull(processInstance)) {
            // 激活
            if (processInstance.isSuspended()) {
                runtimeService
                        .activateProcessInstanceById(instanceId);
            } else {
                runtimeService
                        .suspendProcessInstanceById(instanceId);
            }
        }
    }

    /**
     * 强制结束流程实例
     */
    @Override
    public void stopProcessInstance(String instanceId) {
        ProcessInstance processInstance = runtimeService
                .createProcessInstanceQuery()
                .processInstanceId(instanceId)
                .singleResult();
        if (processInstance == null) {
            throw new CommonException("未查到该流程实例!");
        }
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
        if (Objects.nonNull(bpmnModel)) {
            Process process = bpmnModel.getMainProcess();
            List<EndEvent> endNodes = process.findFlowElementsOfType(EndEvent.class, false);
            if (CollectionUtils.isNotEmpty(endNodes)) {
                String endId = endNodes.get(0).getId();
                List<Execution> executions = runtimeService.createExecutionQuery()
                        .parentId(processInstance.getProcessInstanceId())
                        .list();
                List<String> executionIds = new ArrayList<>();
                executions.forEach(execution -> executionIds.add(execution.getId()));
                runtimeService.createChangeActivityStateBuilder()
                        .moveExecutionsToSingleActivityId(executionIds, endId)
                        .changeState();
            }
        }
    }

    /**
     * 保存启动信息
     *
     * @param businessKey     表单标识
     * @param dataId          数据id
     * @param processInstance 实例信息
     */
    private void saveStartInsInfo(String businessKey, String dataId, String startUserNickName, ProcessInstance processInstance) {
        //保存数据触发信息
        TbBusinessTask businessTask = new TbBusinessTask();
        businessTask.setBusinessKey(businessKey);
        businessTask.setDataId(dataId);
        businessTask.setInstanceId(processInstance.getProcessInstanceId());
        businessTask.setProcDefId(processInstance.getProcessDefinitionId());
        businessTask.setDeployId(processInstance.getDeploymentId());
        taskFormService.save(businessTask);
        List<Task> list = taskService.createTaskQuery()
                .processInstanceId(processInstance.getProcessInstanceId())
                .list();
        //如果开始即结束,保存结束信息
        if (CollUtil.isEmpty(list)) {
            TbFlowEnd flowEnd = TbFlowEnd.builder()
                    .startUserId(processInstance.getStartUserId())
                    .startTime(new Date())
                    .startUserName(startUserNickName)
                    .endTime(new Date())
                    .taskFormId(businessTask.getId()).build();
            flowInfoService.saveFlowEnd(flowEnd);
            return;
        }
        //如果又下个待办
        ArrayList<String> ids = new ArrayList<>();
        ArrayList<String> names = new ArrayList<>();
        for (Task nextTask : list) {
            if (ObjectUtil.isNotNull(nextTask)) {
                ids.add(nextTask.getId());
                names.add(nextTask.getName());
                flowTaskService.sendNextTaskMsg(processInstance.getProcessDefinitionName(), nextTask);
            }
        }
        //保存启动信息
        TbFlowStart flowStart = TbFlowStart.builder()
                .taskFormId(businessTask.getId())
                .startUserId(processInstance.getStartUserId())
                .startUserName(startUserNickName)
                .nextTaskId(StrUtil.join(",", ids))
                .nextTaskName(StrUtil.join(",", names))
                .build();
        flowInfoService.saveFlowStart(flowStart);

    }
}